/*******************************************************************************
** www.IfcGears.com                                                           **
** Copyright (C) 2010 - Bauhaus University Weimar, www.uni-weimar.de          **
** Written: 2010-10-02, Fabian Gerold, www.fabiangerold.de                    **
*******************************************************************************/

#include <assert.h>
#include <set>
#include <string>
#include "gears/IfcGears.h"
#include "schema/entity/Attribute.h"
#include "schema/type/Type.h"
#include "Entity.h"

Entity::Entity( IfcGears* gears )
{
	m_gears = gears;
	m_classname = "";
	m_schema = "";
	m_supertype_name = "";
	m_supertype = NULL;
}

bool Entity::setSchema( QString schema )
{
	m_schema = schema;
	return true;
}

void Entity::setSchemaVersion( QString schema_version, shared_ptr<IfcRelease> ifc_release )
{
	m_schema_version = schema_version;
	m_ifc_release = ifc_release;
}

bool Entity::parse()
{
	QString schema = m_schema;
	QString header_str;

	// extract header: go to first semicolon
	{
		int pos_header_end;
		QRegExp rx_header_end( ";[\\r]?\\n" );
		if( (pos_header_end = rx_header_end.indexIn( schema, 0 ) ) == -1 )
		{
			m_gears->slotEmitTxtOutWarning( "could not detect header correctly in: " + schema );
		}
		else
		{
			QStringRef header_str_ref(&schema, 0, pos_header_end+rx_header_end.matchedLength() );
			header_str = header_str_ref.toString();
			
			QRegExp rx_supertype( "SUPERTYPE" );
			if( rx_supertype.indexIn( header_str, 0 ) != -1 )
			{
				rx_supertype.setPattern( "(?:ABSTRACT)?\\s*SUPERTYPE\\s*OF\\s*\\((?:ONEOF)?\\s*\\([a-zA-Z0-9_,\\s]+\\)\\)" );
				if( rx_supertype.indexIn( header_str, 0 ) != -1 )
				{
					QString complete_match = rx_supertype.cap(0);
					QString supertype = rx_supertype.cap(1);
					m_supertype_name = supertype;
					header_str.remove( rx_supertype );
				}
				else
				{
					m_gears->slotEmitTxtOutWarning( "could not detect SUPERTYPE OF ONEOF correctly in: " + schema );
				}
			}
			
			QRegExp rx_subtype( "SUBTYPE" );
			if( rx_subtype.indexIn( header_str, 0 ) != -1 )
			{
				rx_subtype.setPattern( "SUBTYPE\\s*OF\\s*\\(\\s*([a-zA-Z0-9_]+)\\s*\\);" );
				if( rx_subtype.indexIn( header_str, 0 ) != -1 )
				{
					QString complete_match = rx_subtype.cap(0);
					QString supertype = rx_subtype.cap(1);
					m_supertype_name = supertype;
					header_str.remove( rx_subtype );
				}
				else
				{
					m_gears->slotEmitTxtOutWarning( "could not detect SUBTYPE OF correctly in: " + schema );
				}
			}
			
			QRegExp rx_entity( "\\s*ENTITY\\s*[a-zA-Z0-9_]+\\s*;?\\s*" );
			if( rx_entity.indexIn( header_str, 0 ) != -1 )
			{
				header_str.remove( rx_entity );
			}

			if( header_str.size() > 0 )
			{
				m_gears->slotEmitTxtOutWarning( "remaining header: " + header_str + " in schema: " + schema );
			}
		}
		
		// cut out header
		schema.remove( 0, pos_header_end + rx_header_end.matchedLength() );
	}

	//DERIVE
	//P : LIST [3:3] OF IfcDirection := IfcBuildAxes(Axis, RefDirection);
	//U : LIST [2:2] OF IfcDirection := IfcBaseAxis(2,SELF\IfcCartesianTransformationOperator.Axis1,
    //     SELF\IfcCartesianTransformationOperator.Axis2,?);
	// ClosedCurve : LOGICAL := Segments[NSegments].Transition <> Discontinuous;
	//SELF\IfcGeometricRepresentationContext.WorldCoordinateSystem : IfcAxis2Placement := ParentContext.WorldCoordinateSystem;
	//NumberOfHeadings : INTEGER := SIZEOF(QUERY( Temp <* Rows | Temp.IsHeading));
	{
		QString single_derive( "\\s*([a-zA-Z0-9_\\.\\\\]+)\\s*:\\s*([a-zA-Z0-9_\\-\\[\\]:,\\?<>\\.\\|\\s=\\(\\)\\*\\\\]+);\\n" );
		QRegExp rx_derive( "\\s*DERIVE\\s*\\n(" + single_derive + ")+" );
		int pos_derive = rx_derive.indexIn( schema, 0 );
		if( pos_derive != -1 )
		{
			schema.remove( rx_derive );
		}
	}

	//WHERE
	//WR1 : SELF\IfcPlacement.Location.Dim = 3;
	//WR2 : (NOT (EXISTS (Axis))) OR (Axis.Dim = 3);
	//WR3 : (NOT (EXISTS (RefDirection))) OR (RefDirection.Dim = 3);
	//WR4 : (NOT (EXISTS (Axis))) OR (NOT (EXISTS (RefDirection))) OR (IfcCrossProduct(Axis,RefDirection).Magnitude > 0.0);
	//WR5 : NOT ((EXISTS (Axis)) XOR (EXISTS (RefDirection)));
	{
		QString single_where( "\\s*([a-zA-Z0-9_]+)\\s*:\\s*([a-zA-Z0-9_\\-\\[\\]{}:,\\+\\/\\s=\\|\\.\\(\\)\\<\\>\\'\\*\\\\]+);\\n" );
		QRegExp rx_where( "\\s*WHERE\\s*\\n(" + single_where + ")+" );
		int pos_where = rx_where.indexIn( schema, 0 );
		
		if( pos_where != -1 )
		{
			schema.remove( rx_where );
		}
	}

	//INVERSE
	//Contains : SET [0:?] OF IfcClassificationItem FOR ItemOf;
	//UsingCurves : SET [1:?] OF IfcCompositeCurve FOR Segments;
	{
		QString single_inverse = "\\s*([a-zA-Z0-9_]+)\\s*:\\s*([a-zA-Z0-9_\\?\\[\\]:\\s=\\(\\)]+);\\n*";
		QRegExp rx_inverse( "\\s*INVERSE\\s*\\n(" + single_inverse + ")+" );
		int pos_inverse = rx_inverse.indexIn( schema, 0 );
		if( pos_inverse != -1 )
		{
			parseInverse( rx_inverse.cap(0) );
			schema.remove( rx_inverse );
		}
	}
	//UNIQUE
	//Contains : SET [0:?] OF IfcClassificationItem FOR ItemOf;
	{
		QString rx_unique_single( "\\s*([a-zA-Z0-9_]+)\\s*:\\s*([a-zA-Z0-9_\\s\\[\\]:,\\?]+);\\n*" );
		QRegExp rx_unique( "\\s*UNIQUE\\s*\\n(" + rx_unique_single + ")+" );
		int pos_unique = rx_unique.indexIn( schema, 0 );
		if( pos_unique != -1 )
		{
			QString unique_attributes = rx_unique.cap(0);
			schema.remove( rx_unique );

		}
	}

	// attributes: lines like
	// Item : IfcGeometricRepresentationItem;
	// InnerBoundaries : OPTIONAL SET [1:?] OF IfcCurve;
	// Pixel : LIST [1:?] OF BINARY(32);
	int pos_attribute=0;
	QRegExp rx_attribute( "\\s*([a-zA-Z0-9_]+)\\s*:\\s*([a-zA-Z0-9_\\s\\[\\]:\\?\\(\\)]+);\\n*" );
	while( (pos_attribute = rx_attribute.indexIn( schema, 0 ) ) != -1 )
	{
		QString complete_match = rx_attribute.cap(0);
		QString attribute_name = rx_attribute.cap(1);
		QString attribute_type = rx_attribute.cap(2);
		attribute_type.remove( QRegExp( "^(\\s*)*" ) );

		QString attribute_comment( "" );
		QRegExp rx_optional( "(OPTIONAL\\s+)[a-zA-Z0-9_]+" );
		if( rx_optional.indexIn( attribute_type, 0 ) != -1 )
		{
			attribute_comment = "optional";
			attribute_type.remove( rx_optional.cap(1) );
		}

		bool primitive_attribute = false;
		std::map<QString,QString>::iterator it;
		for( it=m_gears->m_basic_types.begin(); it!=m_gears->m_basic_types.end(); ++it )
		{
			QString type_express = (*it).first;
			QString type_cpp = (*it).second;
			QRegExp rx_express( "(^\\s*)?" + type_express );
			if( rx_express.indexIn( attribute_type, 0 ) != -1 )
			{
				attribute_type.replace( rx_express, type_cpp );
				primitive_attribute = true;
			}

		}

		Attribute::Cardinality cardinality = Attribute::CARDINALITY_SINGLE;

		QRegExp rx_array( "^(OPTIONAL|UNIQUE\\s)?(ARRAY|LIST|SET)\\s" );
		if( rx_array.indexIn( attribute_type, 0 ) != -1 )
		{
			//Segments : LIST [1:?] OF IfcCompositeCurveSegment;
			//RelatedConstraints : LIST [1:?] OF UNIQUE IfcConstraint;
			int pos = 0;
			int count_vec_dim = 0;
			QRegExp rx_vec_dim( "^(OPTIONAL|UNIQUE\\s)?(ARRAY|LIST|SET)\\s\\[([0-9]+):([0-9\\?]+)\\]\\sOF\\s*" );
			while( (pos = rx_vec_dim.indexIn( attribute_type, 0 ) ) != -1 )
			{
				QString optional_unique	= rx_vec_dim.cap(1);
				QString container_type	= rx_vec_dim.cap(2);
				QString min				= rx_vec_dim.cap(3);
				QString max				= rx_vec_dim.cap(4);

				attribute_type.remove( rx_vec_dim );
				++count_vec_dim;
			}
			
			if( count_vec_dim == 1 )
			{
				cardinality = Attribute::CARDINALITY_VECTOR;
			}
			else if( count_vec_dim == 2 )
			{
				cardinality = Attribute::CARDINALITY_VECTOR_2D;
			}
			else if( count_vec_dim == 3 )
			{
				cardinality = Attribute::CARDINALITY_VECTOR_3D;
			}
			else
			{
				m_gears->slotEmitTxtOutWarning( "ARRAY/LIST with dim > 2 not implemented: " + schema );
				return false;
			}

			//QRegExp rx( "(ARRAY|LIST|SET)\\s\\[([0-9]+):([0-9\\?]+)\\]\\sOF(\\sUNIQUE)?\\s([a-zA-Z0-9_\\s\\*]+)([0-9\\(\\)]*)?$" );
			QRegExp rx( "(UNIQUE\\s)?([a-zA-Z0-9_\\s\\*]+)([0-9\\(\\)]*)?$" );
			if( (pos = rx.indexIn( attribute_type, 0 ) ) == -1 )
			{
				m_gears->slotEmitTxtOutWarning( "couln't understand ARRAY/LIST: " + attribute_type );
				return false;
			}
			QString complete_match = rx.cap(0);
			QString unique = rx.cap(1);
			QString datatype = rx.cap(2);

			attribute_type = datatype;
		}

		attribute_type.remove( QRegExp( "^\\s*" ) );

		shared_ptr<Attribute>  att;
		
		if( primitive_attribute )
		{
			att = shared_ptr<AttributePrimitive>( new AttributePrimitive( attribute_type, attribute_name, attribute_comment ) );
			att->m_cardinality = cardinality;
		}
		else
		{
			//QString key = m_schema_version + "::" + attribute_type;
			QString key = attribute_type;

			if( m_ifc_release->m_map_types.find( key ) != m_ifc_release->m_map_types.end() )
			{
				att = shared_ptr<AttributeType>( new AttributeType( m_ifc_release->m_map_types[key], attribute_name, attribute_comment ) );
				att->m_cardinality = cardinality;
			}
			else if( m_ifc_release->m_map_entities.find( key ) != m_ifc_release->m_map_entities.end() )
			{
				att = shared_ptr<AttributeEntity>( new AttributeEntity( m_ifc_release->m_map_entities[key], attribute_name, attribute_comment ) );
				att->m_cardinality = cardinality;
			}
			else
			{
				m_gears->slotEmitTxtOutWarning( "Entity::setSchema: unkown attribute type: " + attribute_type );
			}
		}
		schema.remove( complete_match );
		if( att != NULL )
		{
			m_attributes.push_back( att );
		}
	}

	schema.remove( QRegExp( "[\\r\\n\\s]" ) );
	if( schema.size() > 0 )
	{
		m_gears->slotEmitTxtOutWarning( "remaining schema after parsing: " + schema );
	}

	if( m_gears->m_basic_types.find( m_supertype_name ) != m_gears->m_basic_types.end() )
	{
		m_gears->slotEmitTxtOutWarning( "Entity::parse: elementary supertype of entity: " + header_str );
		return false;
	}
	else if( m_supertype_name.startsWith(  "ARRAY" ) || m_supertype_name.startsWith( "LIST" ) || m_supertype_name.startsWith( "SET" ) )
	{
		m_gears->slotEmitTxtOutWarning( "Entity::parse: couln't understand ARRAY/LIST supertype: " + m_supertype_name );
		return false;
	}
	else if( m_supertype_name.startsWith( "STRING" ) )
	{
		m_gears->slotEmitTxtOutWarning( "Entity::parse: couln't understand STRING supertype: " + m_supertype_name );
		return false;
	}
	else if( m_supertype_name.startsWith( "ENUMERATION" ) )
	{
		m_gears->slotEmitTxtOutWarning( "Entity::parse: couln't understand ENUMERATION supertype: " + m_supertype_name );
		return false;
	}
	else if( m_supertype_name.startsWith( "SELECT" ) )
	{
		m_gears->slotEmitTxtOutWarning( "Entity::parse: couln't understand SELECT supertype: " + m_supertype_name );
		return false;
	}
	else if( m_supertype_name == "" )
	{
		
	}
	else if( m_ifc_release->m_map_entities.find( m_supertype_name ) != m_ifc_release->m_map_entities.end() )
	{
		m_supertype = m_ifc_release->m_map_entities[m_supertype_name];
		assert( m_supertype_name.compare( m_supertype->m_classname )==0);
	}
	else 
	{
		m_gears->slotEmitTxtOutWarning( "Entity::parse: unknown supertype: " + m_supertype_name );
		return false;
	}

	return true;
}

bool Entity::parseInverse( QString inv_code )
{
	QRegExp rx_inv( "^(\\s)*INVERSE\\s*" );
	if( ( rx_inv.indexIn( inv_code, 0 ) ) == -1 )
	{
		return false;
	}
	inv_code.remove( rx_inv );

	int pos_attribute=0;
	QRegExp rx_attribute( "\\s*([a-zA-Z0-9_]+)\\s*:\\s*([a-zA-Z0-9_\\s\\[\\]:\\?\\(\\)]+);\\n*" );
	while( (pos_attribute = rx_attribute.indexIn( inv_code, 0 ) ) != -1 )
	{
		QString complete_match = rx_attribute.cap(0);
		QString attribute_name = rx_attribute.cap(1) + "_inverse";
		QString attribute_type = rx_attribute.cap(2);
		attribute_type.remove( QRegExp( "^(\\s*)*" ) );

		QString attribute_comment;
		QRegExp rx_optional( "(OPTIONAL\\s+)[a-zA-Z0-9_]+" );
		if( rx_optional.indexIn( attribute_type, 0 ) != -1 )
		{
			attribute_comment = "optional";
			attribute_type.remove( rx_optional.cap(1) );
		}

		bool primitive_attribute = false;
		
		std::map<QString,QString>::iterator it;
		for( it=m_gears->m_basic_types.begin(); it!=m_gears->m_basic_types.end(); ++it )
		{
			QString type_express = (*it).first;
			QString type_cpp = (*it).second;
			QRegExp rx_express( "(^\\s*)?" + type_express );
			if( rx_express.indexIn( attribute_type, 0 ) != -1 )
			{
				attribute_type.replace( rx_express, type_cpp );
				primitive_attribute = true;
			}

		}

		Attribute::Cardinality cardinality = Attribute::CARDINALITY_SINGLE;

		QRegExp rx_array( "(ARRAY|LIST|SET)\\s" );
		if( rx_array.indexIn( attribute_type, 0 ) != -1 )
		{
			//Segments : LIST [1:?] OF IfcCompositeCurveSegment;
			//RelatedConstraints : LIST [1:?] OF UNIQUE IfcConstraint;
			int pos = 0;
			QRegExp rx( "(ARRAY|LIST|SET)\\s\\[([0-9]+):([0-9\\?]+)\\]\\sOF(\\sUNIQUE)?\\s([a-zA-Z0-9_\\s\\*]+)([0-9\\(\\)]*)?$" );
			if( (pos = rx.indexIn( attribute_type, 0 ) ) == -1 )
			{
				m_gears->slotEmitTxtOutWarning( "couln't understand ARRAY/LIST: " + attribute_type );
				return false;
			}
			QString complete_match = rx.cap(0);
			QString container_type = rx.cap(1);
			QString min = rx.cap(2);
			QString max = rx.cap(3);
			QString unique = rx.cap(4);
			QString datatype = rx.cap(5);
			
			QRegExp rx_num( "\\([0-9]*\\)" );
			if( rx_num.indexIn( datatype ) != -1 )
			{
				//datatype = "const char";
				int wait=0;
			}

			attribute_type = datatype;
			cardinality = Attribute::CARDINALITY_VECTOR;
		}

		attribute_type.remove( QRegExp( "^\\s*" ) );

		QRegExp rx_for( "([a-zA-Z0-9_]+)\\s*FOR\\s*([a-zA-Z0-9_]*)" );
		if( ( rx_for.indexIn( attribute_type, 0 ) ) != -1 )
		{
			attribute_comment += attribute_type;
			attribute_type = rx_for.cap( 1 );
		}


		shared_ptr<Attribute>  att;
		
		if( primitive_attribute )
		{
			att = shared_ptr<AttributePrimitive>( new AttributePrimitive( attribute_type, attribute_name, attribute_comment ) );
			att->m_cardinality = cardinality;
		}
		else
		{
			//QString key = m_schema_version + "::" + attribute_type;
			QString key = attribute_type;
			if( m_ifc_release->m_map_types.find( key ) != m_ifc_release->m_map_types.end() )
			{
				att = shared_ptr<AttributeType>( new AttributeType( m_ifc_release->m_map_types[key], attribute_name, attribute_comment ) );
				att->m_cardinality = cardinality;
			}
			else if( m_ifc_release->m_map_entities.find( key ) != m_ifc_release->m_map_entities.end() )
			{
				att = shared_ptr<AttributeEntity>( new AttributeEntity( m_ifc_release->m_map_entities[key], attribute_name, attribute_comment ) );
				att->m_cardinality = cardinality;
			}
			else
			{
				m_gears->slotEmitTxtOutWarning( "Entity::setSchema: unkown attribute type: " + attribute_type );
			}
		}

		inv_code.remove( complete_match );
		if( att )
		{
			m_inverse_attributes.push_back( att );
		}
	}

	return true;
}

void Entity::getAllSuperClasses( QStringList& all_super_classes )
{
	if( m_supertype != NULL )
	{
		all_super_classes.append( m_supertype->m_classname );
		m_supertype->getAllSuperClasses( all_super_classes );
	}
	else
	{
		// as Entity, we need to be at least derived from IfcGearsEntity
		all_super_classes.append( "IfcGearsEntity" );
	}
	all_super_classes.removeDuplicates();
}
void Entity::getAllSelectClasses( QStringList& set )
{
	foreach( QString sel, m_select )
	{
		if( m_ifc_release->m_map_types.find(sel) != m_ifc_release->m_map_types.end() )
		{
			shared_ptr<Type>  t = m_ifc_release->m_map_types[sel];
			set.append(t->m_classname);
			t->getAllSelectClasses(set);
		}
		if( m_ifc_release->m_map_entities.find(sel) != m_ifc_release->m_map_entities.end() )
		{
			shared_ptr<Entity>  e = m_ifc_release->m_map_entities[sel];
			set.append(e->m_classname);
			e->getAllSelectClasses(set);
		}
	}
}

// attribute declaration
void Entity::getAllAttributes( QStringList& set_all_attributes, bool include_inherited )
{
	if( include_inherited )
	{
		if( m_supertype != NULL )
		{
			m_supertype->getAllAttributes( set_all_attributes, include_inherited );
		}
	}

	// complex attributes need to come first in header file
	std::vector<shared_ptr<Attribute> > attributes_all( m_attributes );
	std::copy( m_inverse_attributes.begin(), m_inverse_attributes.end(), std::back_inserter(attributes_all));

	for( int i=0; i<attributes_all.size(); ++i )
	{
		shared_ptr<Attribute>  att = attributes_all[i];
		if( dynamic_pointer_cast<AttributeType>( att ) )
		{
			shared_ptr<AttributeType>  att_type = dynamic_pointer_cast<AttributeType>( att );
			shared_ptr<Type>  t = att_type->m_type;
			set_all_attributes.append(t->m_classname);
		}
		else if( dynamic_pointer_cast<AttributeEntity>( att ) )
		{
			shared_ptr<AttributeEntity>  att_entity = dynamic_pointer_cast<AttributeEntity>( att );
			shared_ptr<Entity>  ent = att_entity->m_entity;
			set_all_attributes.append(ent->m_classname);
		}
	}
	set_all_attributes.removeDuplicates();
}


QString Entity::getCodeHeader()
{
	m_select_supertypes.removeDuplicates();
	QStringList set_select_superclasses;

	QString entity_inherit;
	QString additional_includes;

	if( m_select_supertypes.size() > 0 )
	{
		for( int j=0; j<m_select_supertypes.size(); ++j )
		{
			QString sel = m_select_supertypes.at( j );
			if( m_ifc_release->m_map_types.find(sel) != m_ifc_release->m_map_types.end() )
			{
				shared_ptr<Type>  t = m_ifc_release->m_map_types[sel];
				t->getAllSuperClasses(set_select_superclasses);
			}
			if( m_ifc_release->m_map_entities.find(sel) != m_ifc_release->m_map_entities.end() )
			{
				shared_ptr<Entity>  e = m_ifc_release->m_map_entities[sel];
				e->getAllSuperClasses(set_select_superclasses);
			}
		}

		for( int j=0; j<m_select_supertypes.size(); ++j )
		{
			QString sel = m_select_supertypes.at( j );

			if( !set_select_superclasses.contains(sel) )
			{
				if( entity_inherit.size() > 0 )
				{
					entity_inherit += ", public ";
				}
				else
				{
					entity_inherit += " : public ";
				}
				entity_inherit += sel;


				if( m_ifc_release->m_map_types.find(sel)!=m_ifc_release->m_map_types.end())
				{
					shared_ptr<Type>  t = m_ifc_release->m_map_types[sel];
					additional_includes += "#include \"" + t->m_classname + ".h\"\n";
				}
				else if( m_ifc_release->m_map_entities.find(sel)!=m_ifc_release->m_map_entities.end())
				{
					shared_ptr<Entity>  e = m_ifc_release->m_map_entities[sel];
					additional_includes += "#include \"" + e->m_classname + ".h\"\n";
				}
			}
		}
	}

	QString code_h( m_gears->m_header_comment );
	code_h += "#pragma once\n";
	
	code_h += "#include <vector>\n";
	code_h += "#include <map>\n";
	code_h += "#include <sstream>\n";
	code_h += "#include <string>\n";
	code_h += "#include \"ifcgears/model/shared_ptr.h\"\n";
	code_h += "#include \"ifcgears/model/IfcGearsObject.h\"\n";
	code_h += additional_includes;

	
	if( m_supertype != NULL )
	{
		code_h += "#include \"" + m_supertype->m_classname + ".h\"\n";
	}

	if( m_gears->m_ifc_namespace )
		code_h += "namespace " + m_schema_version + "\n{\n";

	// attribute declaration
	QStringList set_forward_declare_classes;
	getAllAttributes( set_forward_declare_classes, false );

	foreach( QString classname, set_forward_declare_classes )
	{
		code_h += "class " + classname + ";\n";
	}

	if( m_supertype )
	{
		if( entity_inherit.size() > 0 )
		{
			entity_inherit += ", public " + m_supertype->m_classname;
		}
		else
		{
			entity_inherit += " : public " + m_supertype->m_classname;
		}

	}
	else
	{
		if( !set_select_superclasses.contains("IfcGearsEntity") )
		{
			if( entity_inherit.size() > 0 )
			{
				entity_inherit += ", public IfcGearsEntity";
			}
			else
			{
				entity_inherit += " : public IfcGearsEntity";
			}
		}
	}
	

	// class definition
	code_h += "//ENTITY\nclass " + m_classname + entity_inherit + "\n{\n";

	// enums
	code_h += "public:\n";
	if( m_enums.size() > 0 )
	{
		throw std::exception( "parseEntity(): unhandled enum " );
	}

	// constructors
	code_h += m_classname + "();\n";
	code_h += m_classname + "( int id );\n";

	// destructor
	code_h += "~" + m_classname + "();\n";

	// methods
	if( m_gears->m_method_setEntity )
	{
		code_h += "\n// method setEntity takes over all attributes from another instance of the class\n";
		code_h += "void setEntity( shared_ptr<" + m_classname + "> other );\n";
	}
	code_h += "virtual void getStepLine( std::stringstream& stream ) const;\n";
	code_h += "virtual void getStepParameter( std::stringstream& stream, bool is_select_type = false ) const;\n";
	code_h += "virtual void readStepData( std::vector<std::string>& args, const std::map<int,shared_ptr<IfcGearsEntity> >& map );\n";

	// classname()
	code_h += "virtual const char* classname() { return \"" + m_classname + "\"; }\n\n";

	int max_strlen = getStrlenAttributesTypes();
	int num_tabs = ceil( max_strlen/4.0 ) + 1;
	num_tabs = std::max( 12, num_tabs );
	code_h += getAttributeDeclaration( num_tabs );

	// end of class
	code_h += "};\n";

	if( m_gears->m_ifc_namespace )
		code_h += "}\n";//namespace
	code_h += "\n";

	return code_h;
}


QString Entity::getCodeCpp()
{
	QString class_name_upper = m_classname.toUpper();
	QString code_cpp( m_gears->m_header_comment );
	
	code_cpp += "#include <sstream>\n";
	code_cpp += "#include <limits>\n\n";
	code_cpp += "#include \"ifcgears/model/IfcGearsException.h\"\n";
	code_cpp += "#include \"ifcgears/reader/ReaderUtil.h\"\n";
	code_cpp += "#include \"ifcgears/writer/WriterUtil.h\"\n";
	code_cpp += "#include \"ifcgears/IFC/EntityEnums.h\"\n";
	//code_cpp += "#include \"include/" + m_schema_version + "EntityEnums.h\"\n";
	
	// attribute declaration
	QStringList set_attribute_includes;
	getAllAttributes( set_attribute_includes, true );

	foreach( QString classname, set_attribute_includes )
	{
		code_cpp += "#include \"include/" + classname + ".h\"\n";
	}
	code_cpp += "#include \"include/" + m_classname + ".h\"\n";

	code_cpp += "\n";

	// comment with class name
	code_cpp += "// ENTITY " + m_classname + " ";
	while( code_cpp.length() <= 80 )
	{
		code_cpp += "/";
	}
	code_cpp += "\n";

	// constructor
	if( m_gears->m_ifc_namespace )
	{
		code_cpp += m_classname + "::" + m_classname + "() { m_entity_enum = " + class_name_upper + "; }\n";
		code_cpp += m_classname + "::" + m_classname + "( int id ) { m_id = id; m_entity_enum = " + class_name_upper + "; }\n";
	}
	else
	{
		code_cpp += m_classname + "::" + m_classname + "() { m_entity_enum = " + class_name_upper + "; }\n";
		code_cpp += m_classname + "::" + m_classname + "( int id ) { m_id = id; m_entity_enum = " + class_name_upper + "; }\n";
	}

	//code_cpp += m_schema_version + "::" +m_classname + "::" + m_classname + "() { m_entity_enum = " + m_schema_version + "_" + class_name_upper + "; }\n";
	//code_cpp += m_schema_version + "::" +m_classname + "::" + m_classname + "( int id ) { m_id = id; m_entity_enum = " + m_schema_version + "_" + class_name_upper + "; }\n";

	// destructor
	if( m_gears->m_ifc_namespace )
		code_cpp += m_schema_version + "::" +m_classname + "::~" + m_classname + "() {}\n";
	else
		code_cpp += m_classname + "::~" + m_classname + "() {}\n";

	// set method
	if( m_gears->m_method_setEntity )
	{
		code_cpp += "\n// method setEntity takes over all attributes from another instance of the class\n";

		if( m_gears->m_ifc_namespace )
			code_cpp += "void " + m_schema_version + "::" + m_classname + "::setEntity( shared_ptr<" + m_classname + "> other )\n{\n";
		else
			code_cpp += "void " + m_classname + "::setEntity( shared_ptr<" + m_classname + "> other )\n{\n";
		code_cpp += getCodeSetEntity();
		code_cpp += "}\n";
	}

	// step writer
	if( m_gears->m_ifc_namespace )
		code_cpp += "void " + m_schema_version + "::" + m_classname + "::getStepLine( std::stringstream& stream ) const\n{\n";
	else
		code_cpp += "void " + m_classname + "::getStepLine( std::stringstream& stream ) const\n{\n";
	code_cpp += "	stream << \"#\" << m_id << \"=" + m_classname.toUpper() + "\" << \"(\";\n";;
	code_cpp += getCodeWriterEntity();
	code_cpp += "	stream << \");\";\n}\n";

	if( m_gears->m_ifc_namespace )
	{
		code_cpp += "void " + m_schema_version + "::" + m_classname + "::getStepParameter( std::stringstream& stream, bool ) const { stream << \"#\" << m_id; }\n";
		code_cpp += "void " + m_schema_version + "::" + m_classname + "::readStepData( std::vector<std::string>& args, const std::map<int,shared_ptr<IfcGearsEntity> >& map )\n{\n";
	}
	else
	{
		code_cpp += "void " + m_classname + "::getStepParameter( std::stringstream& stream, bool ) const { stream << \"#\" << m_id; }\n";
		code_cpp += "void " + m_classname + "::readStepData( std::vector<std::string>& args, const std::map<int,shared_ptr<IfcGearsEntity> >& map )\n{\n";
	}
	
	int iarg = 0;
	code_cpp += getEntityReader( true, iarg );
	code_cpp += "}\n";

	// toCurrentVersion
	// search in newest version for entities with same name
	//code_cpp += "IfcGearsEntity* " + m_schema_version + "::" + m_classname + "::toCurrentVersion() const { stream << \"#\" << m_id; }\n";

	//code_cpp += m_gears->m_namespace_end;

	return code_cpp;
}

QString Entity::getClassDeclaration()
{
	return "class " + m_classname + ";\n";
}

void Entity::linkDependencies()
{
	std::vector<shared_ptr<Attribute> > attributes_all( m_attributes );
	std::copy( m_inverse_attributes.begin(), m_inverse_attributes.end(), std::back_inserter(attributes_all));

	for( int i=0; i<attributes_all.size(); ++i )
	{
		shared_ptr<Attribute>  att = attributes_all[i];
		if( dynamic_pointer_cast<AttributeType>( att ) )
		{
			shared_ptr<AttributeType>  att_type = dynamic_pointer_cast<AttributeType>( att );
			shared_ptr<Type>  t = att_type->m_type;
		}
		else if( dynamic_pointer_cast<AttributeEntity>( att ) )
		{
			shared_ptr<AttributeEntity>  att_entity = dynamic_pointer_cast<AttributeEntity>( att );
			shared_ptr<Entity>  e = att_entity->m_entity;
		}
	}
}


QString Entity::getAttributeDeclaration( int num_tabs, bool only_comment )
{
	QString declaration;
	if( m_supertype != NULL )
	{
		declaration += m_supertype->getAttributeDeclaration( num_tabs, true );
	}


	declaration += "\n// " + m_classname + " -----------------------------------------------------------\n";

	for( int i=0; i<m_attributes.size()+m_inverse_attributes.size(); ++i )
	{
		bool inverse = false;
		shared_ptr<Attribute>  att;
		if( i<m_attributes.size() )
		{
			att = m_attributes[i];
			if( i == 0 )
			{
				declaration += "// attributes:\n";;
			}
		}
		else
		{
			int i_inv = i - (int)m_attributes.size();
			att = m_inverse_attributes[i_inv];
			if( i==m_attributes.size() )
			{
				// first inverse attribute
				if( only_comment )
				{
					declaration += "// inverse attributes:\n";
				}
				else
				{
					declaration += "// inverse attributes:\n";
				}
			}
			inverse = true;
		}

		bool use_weak_ptr = inverse;
		QString datatype = att->getDataTypeWithSmartPointer(use_weak_ptr);

		QString dec_datatype;
		if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
		{
			dec_datatype = "std::vector<" + datatype + " >";
		}
		else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_2D )
		{
			dec_datatype = "std::vector<std::vector<" + datatype + " > >";
		}
		else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_3D )
		{
			dec_datatype = "std::vector<std::vector<std::vector<" + datatype + " > > >";
		}
		else
		{
			dec_datatype = datatype;
		}

		if( only_comment )
		{
			if( inverse )
			{
				declaration += "//  " + dec_datatype;
			}
			else
			{
				declaration += "//  " + dec_datatype;
			}
		}
		else
		{
			declaration += dec_datatype;
		}

		int num_remaining_tabs = num_tabs - ceil( dec_datatype.size()/4.0);
		num_remaining_tabs = std::max( 1, num_remaining_tabs);
		for( int i=0; i<num_remaining_tabs; ++i ) declaration += "\t";
		declaration += "m_" + att->m_name + ";";

		if( att->m_comment != "" )
		{
			int num_tabs_comment = 7 - ceil( att->m_name.size()/4.0 );
			num_tabs_comment = std::max( 1, num_tabs_comment);
			QString indent_comment;
			for( int i=0; i<num_tabs_comment; ++i ) indent_comment += "\t";
			declaration += indent_comment + "//" + att->m_comment;

		}
		declaration += "\n";
	}
	return declaration;
}

int Entity::getNumAttributesIncludingSupertypes()
{
	int num = 0;
	if( m_supertype != NULL )
	{
		num += m_supertype->getNumAttributesIncludingSupertypes();
	}
	num += (int)m_attributes.size();
	return num;
}

int Entity::getStrlenAttributesTypesAndNames()
{
	int size = 1;
	for( int i=0; i<m_attributes.size()+m_inverse_attributes.size(); ++i )
	{
		bool inverse = false;
		shared_ptr<Attribute>  att;
		if( i < m_attributes.size() )
		{
			att = m_attributes[i];
		}
		else
		{
			inverse = true;
			att = m_inverse_attributes[i-m_attributes.size()];
		}

		bool use_weak_ptr = inverse;
		QString datatype = att->getDataTypeWithSmartPointer(use_weak_ptr);

		QString dec1;
		if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
		{
			dec1 = "std::vector<" + datatype + " >";
		}
		else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_2D )
		{
			dec1 = "std::vector<std::vector<" + datatype + " > >";
		}
		else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_3D )
		{
			dec1 = "std::vector<std::vector<std::vector<" + datatype + " > > >";
		}
		else
		{
			dec1 = datatype;
		}

		QString attribute_declaration( dec1 + "m_" + att->m_name + ";" );
		if( attribute_declaration.size() >size )
		{
			size = attribute_declaration.size();
		}
	}


	return size;
}
int Entity::getStrlenAttributesTypes()
{
	int size = 1;
	for( int i=0; i<m_attributes.size()+m_inverse_attributes.size(); ++i )
	{
		bool inverse = false;
		shared_ptr<Attribute>  att;
		if( i < m_attributes.size() )
		{
			att = m_attributes[i];
		}
		else
		{
			inverse = true;
			att = m_inverse_attributes[i-m_attributes.size()];
		}

		bool use_weak_ptr = inverse;
		QString datatype = att->getDataTypeWithSmartPointer(use_weak_ptr);

		QString dec1;
		if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
		{
			dec1 = "std::vector<" + datatype + " >";
		}
		else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
		{
			dec1 = "std::vector<std::vector<" + datatype + " > >";
		}
		else
		{
			dec1 = datatype;
		}

		if( dec1.size() >size )
		{
			size = dec1.size();
		}
	}

	return size;
}

QString Entity::getCodeSetEntity()
{
	QString set_entity = "";

	// supertype attributes
	if( m_supertype != 0 )
	{
		set_entity += m_supertype->getCodeSetEntity();
	}

	// own attributes
	for( int i=0; i<m_attributes.size(); ++i )
	{
		shared_ptr<Attribute>  att = m_attributes[i];
		QString name = att->m_name;

		bool use_weak_ptr = false;
		QString datatype = att->getDataTypeWithSmartPointer(use_weak_ptr);

		if( dynamic_pointer_cast<AttributePrimitive>(att) )
		{
			shared_ptr<AttributePrimitive> att_primitive = dynamic_pointer_cast<AttributePrimitive>(att);
			if( att_primitive->m_cardinality == Attribute::CARDINALITY_SINGLE )
			{
				set_entity += "m_" + att->m_name + " = other->m_" + att->m_name + ";\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
			{
				set_entity += "m_" + att->m_name + " = other->m_" + att->m_name + ";\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_2D )
			{
				set_entity += "m_" + att->m_name + " = other->m_" + att->m_name + ";\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_3D )
			{
				set_entity += "m_" + att->m_name + " = other->m_" + att->m_name + ";\n";
			}
		}
		else if( dynamic_pointer_cast<AttributeType>(att) )
		{

			shared_ptr<AttributeType>  att_type = dynamic_pointer_cast<AttributeType>(att);

			if( att->m_cardinality == Attribute::CARDINALITY_SINGLE )
			{
				set_entity += "m_" + att->m_name + " = other->m_" + att->m_name + ";\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
			{
				set_entity += "m_" + att->m_name + " = other->m_" + att->m_name + ";\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_2D )
			{
				set_entity += "m_" + att->m_name + " = other->m_" + att->m_name + ";\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_3D )
			{
				set_entity += "m_" + att->m_name + " = other->m_" + att->m_name + ";\n";
			}
		}
		else if( dynamic_pointer_cast<AttributeEntity>(att) )
		{

			shared_ptr<AttributeEntity>  att_entity = dynamic_pointer_cast<AttributeEntity>(att);
			if( att->m_cardinality == Attribute::CARDINALITY_SINGLE )
			{
				set_entity += "m_" + att->m_name + " = other->m_" + att->m_name + ";\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
			{
				set_entity += "m_" + att->m_name + " = other->m_" + att->m_name + ";\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_2D )
			{
				set_entity += "m_" + att->m_name + " = other->m_" + att->m_name + ";\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_3D )
			{
				set_entity += "m_" + att->m_name + " = other->m_" + att->m_name + ";\n";
			}
		}
		else
		{
			m_gears->slotEmitTxtOutWarning( "err 73" );
		}
	}

	return set_entity;
}

QString Entity::getCodeWriterEntity()
{
	QString entity_writer = "";

	// supertype attributes
	if( m_supertype != 0 )
	{
		QString supertype_writer = m_supertype->getCodeWriterEntity();
		if( supertype_writer.length() > 0 )
		{
			entity_writer += supertype_writer + "	stream << \",\";\n";
		}
	}

	// own attributes
	for( int i=0; i<m_attributes.size(); ++i )
	{
		shared_ptr<Attribute>  att = m_attributes[i];
		QString name = att->m_name;

		bool use_weak_ptr = false;
		QString datatype = att->getDataTypeWithSmartPointer(use_weak_ptr);

		if( dynamic_pointer_cast<AttributePrimitive>(att) )
		{
			shared_ptr<AttributePrimitive> att_primitive = dynamic_pointer_cast<AttributePrimitive>(att);
			if( att_primitive->m_cardinality == Attribute::CARDINALITY_SINGLE )
			{
				if( att_primitive->m_primitive_type.compare( "bool" ) == 0 )
				{
					entity_writer += "if( m_" + att->m_name + " == false ) { stream << \".F.\"; }\n";
					entity_writer += "else if( m_" + att->m_name + " == true ) { stream << \".T.\"; }\n";
				}
				else if( att_primitive->m_primitive_type.compare( "int" ) == 0 )
				{
					entity_writer += "if( m_" + att->m_name + " == m_" + att->m_name + " ){ stream << m_" + att->m_name + "; }\n";
					entity_writer += "else { stream << \"$\"; }\n";
				}
				else if( att_primitive->m_primitive_type.compare( "double" ) == 0 )
				{
					entity_writer += "if( m_" + att->m_name + " == m_" + att->m_name + " ){ stream << m_" + att->m_name + "; }\n";
					entity_writer += "else { stream << \"$\"; }\n";
				}
				else if( att_primitive->m_primitive_type.compare( "std::string" ) == 0 )
				{
					entity_writer += "stream << m_" + att->m_name + ";\n";
				}
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
			{
				// vector of 
				if( att_primitive->m_primitive_type.compare( "bool" ) == 0 )
				{
					entity_writer += "writeBoolList( stream, m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "int" ) == 0 )
				{
					entity_writer += "writeIntList( stream, m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "double" ) == 0 )
				{
					entity_writer += "writeDoubleList( stream, m_" + name + " );\n";

				}
				else if( att_primitive->m_primitive_type.compare( "const char*" ) == 0 )
				{
					entity_writer += "writeConstCharList( stream, m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "std::string" ) == 0 )
				{
					entity_writer += "writeStringList( stream, m_" + name + " );\n";
				}
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_2D )
			{
				// vector of 
				if( att_primitive->m_primitive_type.compare( "bool" ) == 0 )
				{
					entity_writer += "writeBoolList2D( stream, m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "int" ) == 0 )
				{
					entity_writer += "writeIntList2D( stream, m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "double" ) == 0 )
				{
					entity_writer += "writeDoubleList2D( stream, m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "const char*" ) == 0 )
				{
					entity_writer += "writeConstCharList2D( stream, m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "std::string" ) == 0 )
				{
					entity_writer += "writeStringList2D( stream, m_" + name + " );\n";
				}
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_3D )
			{
				// vector of 
				if( att_primitive->m_primitive_type.compare( "bool" ) == 0 )
				{
					entity_writer += "writeBoolList3D( stream, m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "int" ) == 0 )
				{
					entity_writer += "writeIntList3D( stream, m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "double" ) == 0 )
				{
					entity_writer += "writeDoubleList3D( stream, m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "const char*" ) == 0 )
				{
					entity_writer += "writeConstCharList3D( stream, m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "std::string" ) == 0 )
				{
					entity_writer += "writeStringList3D( stream, m_" + name + " );\n";
				}
			}
		}
		else if( dynamic_pointer_cast<AttributeType>(att) )
		{

			shared_ptr<AttributeType>  att_type = dynamic_pointer_cast<AttributeType>(att);

			if( att->m_cardinality == Attribute::CARDINALITY_SINGLE )
			{
				if( att_type->m_type->m_select.size() > 0 )
				{
					//entity_writer += "if( m_" + att->m_name + " ) { ";
					//entity_writer += "stream << m_" + att->m_name + "->classname() << \"(\"; ";
					//entity_writer += "m_" + att->m_name + "->getStepParameter( stream, true ); ";
					//entity_writer += "stream << \")\"; } else { stream << \"$\"; }\n";

					entity_writer += "if( m_" + att->m_name + " ) { m_" + att->m_name + "->getStepParameter( stream, true ); } else { stream << \"$\"; }\n";
				}
				else
				{
					entity_writer += "if( m_" + att->m_name + " ) { m_" + att->m_name + "->getStepParameter( stream ); } else { stream << \"$\"; }\n";
				}
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
			{
				if( att_type->m_type->m_super_primitive.compare( "double" ) == 0 )
				{
					entity_writer += "writeTypeOfRealList( stream, m_" + name + " );\n";
				}
				else if( att_type->m_type->m_super_primitive.compare( "int" ) == 0 )
				{
					entity_writer += "writeTypeOfIntList( stream, m_" + name + " );\n";
				}
				else
				{
					if( att_type->m_type->m_select.size() > 0 )
					{
						entity_writer += "writeTypeList( stream, m_" + name + ", true );\n";
					}
					else
					{
						entity_writer += "writeTypeList( stream, m_" + name + " );\n";
					}
				}
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_2D )
			{
				if( att_type->m_type->m_super_primitive.compare( "double" ) == 0 )
				{
					entity_writer += "writeTypeOfRealList2D( stream, m_" + name + " );\n";
				}
				else if( att_type->m_type->m_super_primitive.compare( "int" ) == 0 )
				{
					entity_writer += "writeTypeOfIntList2D( stream, m_" + name + " );\n";
				}
				else
				{
					//entity_writer += "writeTypeList2D( stream, m_" + name + " );\n";
					if( att_type->m_type->m_select.size() > 0 )
					{
						entity_writer += "writeTypeList2D( stream, m_" + name + ", true );\n";
					}
					else
					{
						entity_writer += "writeTypeList2D( stream, m_" + name + " );\n";
					}

				}
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_3D )
			{
				if( att_type->m_type->m_super_primitive.compare( "double" ) == 0 )
				{
					entity_writer += "writeTypeOfRealList3D( stream, m_" + name + " );\n";
				}
				else if( att_type->m_type->m_super_primitive.compare( "int" ) == 0 )
				{
					entity_writer += "writeTypeOfIntList3D( stream, m_" + name + " );\n";
				}
				else
				{
					//entity_writer += "writeTypeList3D( stream, m_" + name + " );\n";
					if( att_type->m_type->m_select.size() > 0 )
					{
						entity_writer += "writeTypeList3D( stream, m_" + name + ", true );\n";
					}
					else
					{
						entity_writer += "writeTypeList3D( stream, m_" + name + " );\n";
					}
				}
			}
		}
		else if( dynamic_pointer_cast<AttributeEntity>(att) )
		{

			shared_ptr<AttributeEntity>  att_entity = dynamic_pointer_cast<AttributeEntity>(att);
			if( att->m_cardinality == Attribute::CARDINALITY_SINGLE )
			{
				// single entity attribute
			//	QString full_name = att_entity->m_entity->m_schema_version + "::" + att_entity->m_entity->m_classname;
				if( m_ifc_release->m_map_entities.find(att_entity->m_entity->m_classname) == m_ifc_release->m_map_entities.end() )
				{
					m_gears->slotEmitTxtOutWarning( "err 62" );
				}

				entity_writer += "if( m_" + att->m_name + " ) { stream << \"#\" << m_" + att->m_name + "->getId(); } else { stream << \"$\"; }\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
			{
				entity_writer += "writeEntityList( stream, m_" + name + " );\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_2D )
			{
				entity_writer += "writeEntityList2D( stream, m_" + name + " );\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_3D )
			{
				entity_writer += "writeEntityList3D( stream, m_" + name + " );\n";
			}
		}
		else
		{
			m_gears->slotEmitTxtOutWarning( "err 63" );
		}
		entity_writer += "stream << \",\";\n";
	}
	QString check1 = entity_writer.right( 15 );
	entity_writer = entity_writer.left( entity_writer.length() - 15 );
	QString check2 = entity_writer.right( 15 );

	return entity_writer;
}


QString Entity::getEntityReader( bool lowermost, int& iarg )
{
	QString entity_reader;

	if( lowermost )
	{
		if( iarg != 0 )
		{
			emit( m_gears->slotEmitTxtOutWarning( "lowermost & iarg!= 0" ) );
		}
		// use default constructor
		int num_all_attributes = getNumAttributesIncludingSupertypes();

		if( num_all_attributes > 0 )
		{
			entity_reader += "const int num_args = (int)args.size();\n";
			QString num_str = QString::number( num_all_attributes );
			entity_reader += "if( num_args<" + num_str + " ){ std::stringstream strserr; strserr << \"Wrong parameter count for entity " + m_classname + ", expecting " + num_str + ", having \" << num_args << \". Object id: \" << getId() << std::endl; throw IfcGearsException( strserr.str().c_str() ); }\n";
		}
	}

	// supertype attributes
	if( m_supertype != 0 )
	{
		entity_reader += m_supertype->getEntityReader( false, iarg );
	}

	// own attributes
	for( int i=0; i<m_attributes.size(); ++i )
	{
		shared_ptr<Attribute>  att = m_attributes[i];
		QString name = att->m_name;
		
		bool use_weak_ptr = false;
		QString datatype = att->getDataTypeWithSmartPointer(use_weak_ptr);

		QString iarg_str = QString::number(iarg);

		if( dynamic_pointer_cast<AttributePrimitive>(att) )
		{
			shared_ptr<AttributePrimitive> att_primitive = dynamic_pointer_cast<AttributePrimitive>(att);
			if( att_primitive->m_cardinality == Attribute::CARDINALITY_SINGLE )
			{
				if( att_primitive->m_primitive_type.compare( "bool" ) == 0 )
				{
					entity_reader += "if( _stricmp( args["+ iarg_str + "].c_str(), \".F.\" ) == 0 ) { m_" + name + " = false; }\n";
					entity_reader += "else if( _stricmp( args["+ iarg_str + "].c_str(), \".T.\" ) == 0 ) { m_" + name + " = true; }\n";
				}
				else if( att_primitive->m_primitive_type.compare( "int" ) == 0 )
				{
					entity_reader += "readIntValue( args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "double" ) == 0 )
				{
					entity_reader += "readRealValue( args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "std::string" ) == 0 )
				{
					entity_reader += "m_" + name + " = args[" + iarg_str + "];\n";
				}
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
			{
				// vector of 
				if( att_primitive->m_primitive_type.compare( "bool" ) == 0 )
				{
					entity_reader += "readBoolList(  args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "int" ) == 0 )
				{
					entity_reader += "readIntList(  args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "double" ) == 0 )
				{
					entity_reader += "readDoubleList( args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "const char*" ) == 0 )
				{
					entity_reader += "readConstCharList( args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "std::string" ) == 0 )
				{
					entity_reader += "readStringList( args[" + iarg_str + "], m_" + name + " );\n";
				}
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_2D )
			{
				// vector of 
				if( att_primitive->m_primitive_type.compare( "bool" ) == 0 )
				{
					entity_reader += "readBoolList2D(  args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "int" ) == 0 )
				{
					entity_reader += "readIntList2D(  args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "double" ) == 0 )
				{
					entity_reader += "readDoubleList2D( args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "const char*" ) == 0 )
				{
					entity_reader += "readConstCharList2D( args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "std::string" ) == 0 )
				{
					entity_reader += "readStringList2D( args[" + iarg_str + "], m_" + name + " );\n";
				}
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_3D )
			{
				// vector of 
				if( att_primitive->m_primitive_type.compare( "bool" ) == 0 )
				{
					entity_reader += "readBoolList3D(  args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "int" ) == 0 )
				{
					entity_reader += "readIntList3D(  args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "double" ) == 0 )
				{
					entity_reader += "readDoubleList3D( args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "const char*" ) == 0 )
				{
					entity_reader += "readConstCharList3D( args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_primitive->m_primitive_type.compare( "std::string" ) == 0 )
				{
					entity_reader += "readStringList3D( args[" + iarg_str + "], m_" + name + " );\n";
				}
			}
		}
		else if( dynamic_pointer_cast<AttributeType>(att) )
		{
			shared_ptr<AttributeType>  att_type = dynamic_pointer_cast<AttributeType>(att);

			if( att->m_cardinality == Attribute::CARDINALITY_SINGLE )
			{
				QString reader_args = "args[" + iarg_str + "]";
				if( att_type->m_type->m_select.size() > 0 )
				{
					reader_args = "args[" + iarg_str + "], map";
				}
				entity_reader += "m_" + name +" = " + att_type->m_type->m_classname + "::readStepData( " + reader_args + " );\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
			{
				if( att_type->m_type->m_super_primitive.compare( "double" ) == 0 )
				{
					entity_reader += "readTypeOfRealList( args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_type->m_type->m_super_primitive.compare( "int" ) == 0 )
				{
					entity_reader += "readTypeOfIntList( args[" + iarg_str + "], m_" + name + " );\n";
				}
				//else if( att_type->m_type->m_super_primitive.compare( "std::string" ) == 0 )
				//{
				//	entity_reader += "readTypeOfStringList( args[" + iarg_str + "], m_" + name + " );\n";
				//}
				else
				{
					if( att_type->m_type->m_select.size() > 0 )
					{
						entity_reader += "readSelectList( args[" + iarg_str + "], m_" + name + ", map );\n";
					}
					else
					{
						//entity_reader += "readEntityReferenceList( args[" + iarg_str + "], m_" + name + ", map );\n";
						entity_reader += "readTypeList( args[" + iarg_str + "], m_" + name + " );\n";
					}
				}
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_2D )
			{
				if( att_type->m_type->m_super_primitive.compare( "double" ) == 0 )
				{
					entity_reader += "readTypeOfRealList2D( args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_type->m_type->m_super_primitive.compare( "int" ) == 0 )
				{
					entity_reader += "readTypeOfIntList2D( args[" + iarg_str + "], m_" + name + " );\n";
				}
				else
				{
					entity_reader += "readEntityReferenceList2D( args[" + iarg_str + "], m_" + name + ", map );\n";
				}
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_3D )
			{
				if( att_type->m_type->m_super_primitive.compare( "double" ) == 0 )
				{
					entity_reader += "readTypeOfRealList3D( args[" + iarg_str + "], m_" + name + " );\n";
				}
				else if( att_type->m_type->m_super_primitive.compare( "int" ) == 0 )
				{
					entity_reader += "readTypeOfIntList3D( args[" + iarg_str + "], m_" + name + " );\n";
				}
				else
				{
					entity_reader += "readEntityReferenceList3D( args[" + iarg_str + "], m_" + name + ", map );\n";
				}
			}
		}
		else if( dynamic_pointer_cast<AttributeEntity>(att) )
		{

			shared_ptr<AttributeEntity>  att_entity = dynamic_pointer_cast<AttributeEntity>(att);
			if( att->m_cardinality == Attribute::CARDINALITY_SINGLE )
			{
				// single entity attribute
				//QString full_name = att_entity->m_entity->m_schema_version + "::" + att_entity->m_entity->m_classname;
				if( m_ifc_release->m_map_entities.find(att_entity->m_entity->m_classname) == m_ifc_release->m_map_entities.end() )
				{
					m_gears->slotEmitTxtOutWarning( "err 72" );
				}

				entity_reader += "readEntityReference( args[" + iarg_str + "], m_" + name + ", map );\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
			{
				entity_reader += "readEntityReferenceList( args[" + iarg_str + "], m_" + name + ", map );\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_2D )
			{
				entity_reader += "readEntityReferenceList2D( args[" + iarg_str + "], m_" + name + ", map );\n";
			}
			else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_3D )
			{
				entity_reader += "readEntityReferenceList3D( args[" + iarg_str + "], m_" + name + ", map );\n";
			}
		}
		else
		{
			m_gears->slotEmitTxtOutWarning( "err 73" );
		}
		++iarg;
	}
	return entity_reader;
}

/*
void Entity::generateCodeUnlink()
{
	//m_unlink_cpp = "void " + m_name + "::unlinkAttributes()\n{\n";
	m_unlink_cpp = "";

	int it_count=0;
	int num_attributes_all = (int)(m_attributes.size()+m_inverse_attributes.size());
	for( int i=0; i<num_attributes_all; ++i )
	{
		shared_ptr<Attribute>  att;
		if( i<m_attributes.size() )
		{
			att = m_attributes[i];
		}
		else
		{
			int i_inv = i - (int)m_attributes.size();
			att = m_inverse_attributes[i_inv];
		}

		if( att->m_cardinality == Attribute::CARDINALITY_SINGLE )
		{
			if( dynamic_cast<AttributePrimitive*>(att) )
			{
				continue;
			}
			else
			{
				m_unlink_cpp += "m_" + att->m_name + " = NULL;\n";
			}
		}

		if( att->m_cardinality == Attribute::CARDINALITY_VECTOR )
		{
			if( dynamic_cast<AttributePrimitive*>(att) )
			{
				m_unlink_cpp += "	m_" + att->m_name + ".clear();\n";
			}
			else
			{	
				m_unlink_cpp += "	unlinkVector( (std::vector<weak_ptr<IfcGearsObject> >& )m_" + att->m_name + " );\n";
			}
		}
		else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_2D )
		{
			if( dynamic_cast<AttributePrimitive*>(att) )
			{
				m_unlink_cpp += "	m_" + att->m_name + ".clear();\n";
			}
			else
			{
				m_unlink_cpp += "	unlinkVector2D( (std::vector<std::vector<weak_ptr<IfcGearsObject> > >& )m_" + att->m_name + " );\n";
			}
		}
		else if( att->m_cardinality == Attribute::CARDINALITY_VECTOR_3D )
		{
			if( dynamic_cast<AttributePrimitive*>(att) )
			{
				m_unlink_cpp += "	m_" + att->m_name + ".clear();\n";
			}
			else
			{
				m_unlink_cpp += "	unlinkVector3D( (std::vector<std::vector<std::vector<weak_ptr<IfcGearsObject> > > >& )m_" + att->m_name + " );\n";
			}
		}
	}
}
*/
